package com.sun.springbootfiledownload.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.sun.springbootfiledownload.result.AliyunOssResult;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.List;

/**
 * @version 1.0
 * @Package: com.sun.springbootfiledownload.utils
 * @ClassName: AliyunOSSUtil
 * @Author: sgq
 * @Date: 2025/4/9 10:55
 * @Description: TODD
 */
@Component
@Slf4j
public class AliyunOssUtil {

    @Value("${aliyunOss.endpoint}")
    private String endpoint;
    @Value("${aliyunOss.accessKeyId}")
    private String accessKeyId;
    @Value("${aliyunOss.accessKeySecret}")
    private String accessKeySecret;
    @Value("${aliyunOss.bucketName}")
    private String bucketName;
    @Value("${aliyunOss.urlPrefix}")
    private String urlPrefix;

    private OSS ossClient;

    /**
     * 初始化OssClient
     */
    @PostConstruct
    public void generateOSS() {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        if (ossClient != null) {
            this.ossClient = ossClient;
        } else {
            log.error("OSS对象实例化失败.");
            throw new RuntimeException("OSS对象实例化失败.");
        }
    }

    /**
     * 判断阿里云bucket下是否存在filePath文件夹，不存在则创建
     *
     * @param filePath
     */
    public void isExistAndCreateFolder(String filePath) throws FileUploadException {
        if (!isExists(filePath)) {
            boolean mkdirs = createFolder(filePath);
            if (!mkdirs) {
                throw new FileUploadException("附件文件夹创建失败!");
            }
        }
    }

    /**
     * 上传文件，以IO流方式
     *
     * @param inputStream 输入流
     * @param objectName  唯一objectName（在oss中的文件名字）
     */
    public AliyunOssResult upload(InputStream inputStream, String objectName) {
        AliyunOssResult aliyunOssResult = new AliyunOssResult();
        try {
            // 上传内容到指定的存储空间（bucketName）并保存为指定的文件名称（objectName）。
            PutObjectResult putObject = ossClient.putObject(bucketName, objectName, inputStream);
            // 关闭OSSClient。
//            ossClient.shutdown();
            aliyunOssResult.setCode(200);
            aliyunOssResult.setUrl(urlPrefix + objectName);
            aliyunOssResult.setMsg("上传成功");
        } catch (Exception e) {
            e.printStackTrace();
            aliyunOssResult.setCode(400);
            aliyunOssResult.setMsg("上传失败");
        }
        return aliyunOssResult;
    }

    /**
     * 获取oss文件
     *
     * @param folderName
     * @return
     */
    public OSSObject get(String folderName) {
        OSSObject ossObject = ossClient.getObject(bucketName, folderName);
        return ossObject;
    }

    /**
     * 删除OSS中的单个文件
     *
     * @param objectName 唯一objectName（在oss中的文件名字）
     */
    public void delete(String objectName) {
        try {
            ossClient.deleteObject(bucketName, objectName);
            // 关闭OSSClient。
            ossClient.shutdown();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 批量删除OSS中的文件
     *
     * @param objectNames oss中文件名list
     */
    public void delete(List<String> objectNames) {
        try {
            // 批量删除文件。
            DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(objectNames));
            List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
            // 关闭OSSClient。
            ossClient.shutdown();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取文件临时url
     *
     * @param objectName    oss中的文件名
     * @param effectiveTime 有效时间(ms)
     */
    public String getUrl(String objectName, long effectiveTime) {
        // 设置URL过期时间
        Date expiration = new Date(new Date().getTime() + effectiveTime);
        GeneratePresignedUrlRequest generatePresignedUrlRequest;
        generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, objectName);
        generatePresignedUrlRequest.setExpiration(expiration);
        URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest);
        return url.toString();
    }

    /**
     * oss拷贝文件
     *
     * @param sourcePath
     * @param targetPath
     */
    public void copyFileSourceToTarget(String sourcePath, String targetPath) throws FileNotFoundException, FileUploadException {
        try {
            CopyObjectResult copyObjectResult = ossClient.copyObject(bucketName, sourcePath, bucketName, targetPath);
        } catch (Exception e) {
            throw new FileUploadException("文件转移操作异常.");
        }
    }

    /**
     * 根据文件路径获取输出流
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    public InputStream getInputStream(String filePath) throws IOException {
        if (filePath == null || filePath.isEmpty()) {
            log.error("方法[getInputStream]参数[filePath]不能为空.");
            return null;
        }
        OSSObject object = ossClient.getObject(bucketName, filePath);
        InputStream input = object.getObjectContent();
        byte[] bytes = toByteArray(input);
        return new ByteArrayInputStream(bytes);
    }

    /**
     * InputStream流转byte数组
     *
     * @param input
     * @return
     * @throws IOException
     */
    private static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[input.available()];
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }

    /**
     * 创建文件夹
     *
     * @param folderName
     * @return
     */
    private Boolean createFolder(String folderName) {
        if (folderName == null || folderName.isEmpty()) {
            log.error("方法[createFolder]参数[folderName]不能为空.");
            return false;
        }
        // 防止folderName文件夹因为末尾未加【/】导致将目录当做文件创建
        if (!folderName.substring(folderName.length() - 1).equals("/")) {
            folderName = folderName + "/";
        }
        // 创建文件夹
        try {
            ossClient.putObject(bucketName, folderName, new ByteArrayInputStream(new byte[0]));
            log.info("附件文件夹[" + folderName + "]创建成功.");
            return true;
        } catch (Exception e) {
            log.error("附件文件夹[" + folderName + "]创建失败.");
            return false;
        }
    }

    /**
     * 判断文件夹是否存在
     *
     * @param folderName
     * @return
     */
    private Boolean isExists(String folderName) {
        return ossClient.doesObjectExist(bucketName, folderName);
    }

    /**
     * 根据文件路径获取File
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    public File getFile(String filePath) throws IOException {
        if (filePath == null || filePath.isEmpty()) {
            log.error("方法[getFile]参数[filePath]不能为空.");
            return null;
        }
        File file = new File(filePath);
        InputStream inputStream = getInputStream(filePath);
        copyInputStreamToFile(inputStream, file);
        return file;
    }

    /**
     * InputStream -> File
     *
     * @param inputStream
     * @param file
     * @throws IOException
     */
    private static void copyInputStreamToFile(InputStream inputStream, File file) throws IOException {
        try (FileOutputStream outputStream = new FileOutputStream(file)) {
            int read;
            byte[] bytes = new byte[1024];
            while ((read = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
        }
    }


}